package engine.render;

import engine.util.ShaderUtil;
import java.nio.FloatBuffer;
import java.util.TreeMap;
import math.vector.vec3f;
import static org.lwjgl.opengl.GL20.*;

public class Shader 
{
    
    private int programID;
    //<editor-fold defaultstate="collapsed" desc="getProgramID">
    public int getProgramID()
    {
        return this.programID;
    }
    //</editor-fold>
    
    private String fileName;
    //<editor-fold defaultstate="collapsed" desc="getShaderName">
    public String getShaderName()
    {
        return this.fileName;
    }
    //</editor-fold>
    
    private int vertShaderID;
    private int fragShaderID;
    
    private TreeMap<String, Integer> uniformLocs;
    
    public Shader(String filename) 
    {
        this.fileName = filename;
        this.uniformLocs = new TreeMap<>();
        this.programID = ShaderUtil.get().createShaderProgram();
        this.vertShaderID = ShaderUtil.get().createVertShader(this.fileName);
        this.fragShaderID = ShaderUtil.get().createFragShader(this.fileName);
        glAttachShader(this.programID, this.vertShaderID);
        glAttachShader(this.programID, this.fragShaderID);
        glLinkProgram(this.programID);
        glValidateProgram(this.programID);
        this.unbind();
    }
    
    public final void bind() 
    {
        glUseProgram(this.programID);
    }
    
    public final void unbind() 
    {
        glUseProgram(0);
    }
    
    //<editor-fold defaultstate="collapsed" desc="bindUniform[1i/1f/3f/vec3f/mat4f]">
    public void bindUniform(String uniformName, int val)
    {
        if (this.hasUniform(uniformName))
        {
            glUniform1i(this.uniformLocs.get(uniformName), val);
        }
    }
    
    public void bindUniform(String uniformName, float val)
    {
        if (this.hasUniform(uniformName))
        {
            glUniform1f(this.uniformLocs.get(uniformName), val);
        }
    }
    
    public void bindUniform(String uniformName, float x0, float x1, float x2)
    {
        if (this.hasUniform(uniformName))
        {
            glUniform3f(this.uniformLocs.get(uniformName), x0, x1, x2);
        }
    }
    
    public void bindUniform(String uniformName, vec3f values)
    {
        this.bindUniform(uniformName, values.x, values.y, values.z);
    }
    
    public void bindUniform(String uniformName, FloatBuffer matrix)
    {
        if (this.hasUniform(uniformName))
        {
            glUniformMatrix4(this.uniformLocs.get(uniformName), false, matrix);
        }
    }
    //</editor-fold>
    
    private boolean hasUniform(String uniformName)
    {
        if(!this.uniformLocs.containsKey(uniformName))
        {
            int location = glGetUniformLocation(this.programID, uniformName);
            if (location < 0)
            {
                return false;
            }
            this.uniformLocs.put(uniformName, location);
        }
        return true;
    }
    
    public void destroy()
    {
        this.unbind();
        this.uniformLocs.clear();
        this.uniformLocs = null;
        glDetachShader(this.programID, this.vertShaderID);
        glDetachShader(this.programID, this.fragShaderID);
        glDeleteProgram(this.programID);
        this.programID = -1;
        this.vertShaderID = -1;
        this.fragShaderID = -1;
    }
    
}